from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import time
from selenium.webdriver.chrome.service import Service
import random
from selenium.common.exceptions import TimeoutException, NoSuchElementException, StaleElementReferenceException
import logging


# 配置参数 (需根据实际网页元素修改)
TARGET_URL = "http://huxispce.cqu.edu.cn/pages/subPack/ticket/detail?venueId=1715243260928196609&itemId=7"
REMAIN_TICKET_XPATH = "//div[contains(@class,'remaining')]"  # 剩余票数元素
BOOK_BUTTON_XPATH = "//button[contains(text(),'订票')]"       # 订票按钮
AGREEMENT_CHECK_XPATH = "//input[@type='checkbox']"          # 协议勾选框
CONFIRM_BUTTON_XPATH = "//button[text()='立即预订']"         # 最终确认按钮
# 日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[logging.FileHandler('ticket.log'), logging.StreamHandler()]
)
# 增强配置参数
CONFIG = {
    "target_url": "http://huxispce.cqu.edu.cn/pages/subPack/ticket/detail?venueId=1715243260928196609&itemId=7",
    "elements": {
        "remaining_ticket": ("xpath", "//div[contains(@class,'remaining') and contains(@data-testid,'ticket-count')]"),  # 增加data属性校验
        "book_button": ("xpath", "//button[contains(@class,'buy-btn') and contains(text(),'订票')]"),  # 复合条件
        "agreement_check": ("css", "input.agreement-checkbox[name='user-agreement']"),  # 更精确的CSS选择器
        "confirm_button": ("xpath", "//button[@id='confirmBtn' and contains(text(),'立即预订')]"),
        "success_indicator": ("css", "div.booking-success > h1")
    },
    "wait_params": {
        "base_timeout": 20,
        "poll_frequency": 0.5
    },
    "human_behavior": {
        "min_delay": 0.8,
        "max_delay": 3.8,
        "scroll_variants": [200, 400, -100]  # 随机滚动距离
    }
}
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
wait = WebDriverWait(driver, CONFIG['wait_params']['base_timeout'], CONFIG['wait_params']['poll_frequency'])



ACCOUNT = "500101200603059551"  # 在此替换为实际账号
PASSWORD = "FuMan20060305"  # 在此替换为实际密码

        # 初始化浏览器（示例使用Chrome
try:
        driver.get("https://sso.cqu.edu.cn/login")
        
        # 显式等待框架加载
        wait = WebDriverWait(driver, 15)
        
        # 新版元素定位策略
        username_input = wait.until(EC.presence_of_element_located(
            (By.XPATH, "//input[@name='username' and @placeholder='统一身份认证号/身份证件号']")
        ))
        password_input = wait.until(EC.presence_of_element_located(
            (By.XPATH, "//input[@type='password' and @placeholder='请输入密码']")
        ))
        
        # 特殊输入处理（Angular框架需要触发变更检测）
        username_input.send_keys(ACCOUNT)
        driver.execute_script("arguments[0].dispatchEvent(new Event('input'))", username_input)
        
        password_input.send_keys(PASSWORD)
        driver.execute_script("arguments[0].dispatchEvent(new Event('input'))", password_input)
        
        # 等待按钮解除禁用（关键修改）
        login_button = wait.until(lambda d: d.find_element(
            By.XPATH, 
            "//button[contains(@class, 'login-button') and not(@disabled)]"  # 等待禁用状态解除
        ))

        # 三维点击保障
        driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", login_button)
        login_button.click()
        driver.execute_script("arguments[0].click();", login_button)  # JavaScript兜底点击

        # 验证登录
        WebDriverWait(driver, 10).until(
            EC.url_contains("portal") | EC.title_contains("个人中心")
        )
        print("登录流程执行成功")

except Exception as e:
        print(f"操作失败：{str(e)}")
        driver.save_screenshot('error.png')
finally:
        print("登录成功")

wait = WebDriverWait(driver, CONFIG['wait_params']['base_timeout'], CONFIG['wait_params']['poll_frequency'])

def human_like_delay(base=1.0, variance=0.3):
    """更自然的人类行为延迟"""
    time.sleep(max(base + random.uniform(-variance, variance), 0.5))

def intelligent_refresh():
    """增强型智能刷新策略"""
    try:
        driver.get(CONFIG['target_url'])
        
        # 随机滚动页面
        driver.execute_script(f"window.scrollBy(0, {random.choice(CONFIG['human_behavior']['scroll_variants'])})")
        
        # 动态等待页面加载完成
        wait.until(EC.presence_of_element_located(
            (By.CSS_SELECTOR, "div.main-content")
        ))
        
        # 随机化等待时间
        human_like_delay(1.5, 0.5)
        
    except Exception as e:
        logging.error(f"刷新页面失败: {str(e)}")
        driver.refresh()

def check_availability():
    """增强票数检测逻辑"""
    try:
        elem_type, locator = CONFIG['elements']['remaining_ticket']
        elements = wait.until(
            EC.presence_of_all_elements_located((elem_type, locator))
        )
        
        # 防过时元素处理
        try:
            current_count = int(elements[0].text.split("：")[-1].strip())
        except StaleElementReferenceException:
            elements = driver.find_elements(elem_type, locator)
            current_count = int(elements[0].text.split("：")[-1].strip())

        if current_count > 0:
            logging.info(f"🔥 检测到 {current_count} 张余票，开始抢票！")
            return True
            
    except (NoSuchElementException, TimeoutException) as e:
        logging.warning(f"票数检测异常: {str(e)}")
    except Exception as e:
        logging.error(f"未知错误: {str(e)}")
        driver.save_screenshot('error_check.png')
        
    return False

def booking_flow():
    try:
        # 第一阶段：点击订票
        elem_type, locator = CONFIG['elements']['book_button']
        book_btn = wait.until(
            EC.element_to_be_clickable((elem_type, locator)))
        
        # 人类化点击（带随机偏移）
        action = webdriver.ActionChains(driver)
        action.move_to_element_with_offset(book_btn, random.randint(3,10), random.randint(3,10))
        action.click().perform()
        logging.info("✅ 已点击订票按钮")

        # 第二阶段：窗口切换
        def window_handles_increased(d):
            return len(d.window_handles) > 1
            
        wait.until(window_handles_increased)
        driver.switch_to.window(driver.window_handles[-1])
        logging.info("🔄 已切换到订票窗口")

        # 第三阶段：协议处理
        logging.info("⏳ 正在等待协议加载...")
        elem_type, locator = CONFIG['elements']['agreement_check']
        agreement = wait.until(
            EC.element_to_be_clickable((elem_type, locator)))
        
        # 动态阅读时间
        for _ in range(random.randint(8,15)):
            if not agreement.is_selected():
                agreement.click()
            driver.execute_script("window.scrollBy(0, 50)")
            human_like_delay(0.5, 0.2)
            
        logging.info("✅ 已同意协议条款")

        # 第四阶段：最终确认
        elem_type, locator = CONFIG['elements']['confirm_button']
        confirm_btn = wait.until(
            EC.element_to_be_clickable((elem_type, locator)))
        
        # 双重点击保障
        confirm_btn.click()
        driver.execute_script("arguments[0].click()", confirm_btn)
        logging.info("🚀 已提交预订请求")

        # 第五阶段：结果验证
        elem_type, locator = CONFIG['elements']['success_indicator']
        success_element = wait.until(
            EC.visibility_of_element_located((elem_type, locator)))
        logging.info(f"🎉 订票成功！验证信息: {success_element.text[:20]}...")
        return True

    except TimeoutException as e:
        logging.error("❗ 操作超时，可能页面加载失败")
        driver.save_screenshot('timeout.png')
    except Exception as e:
        logging.error(f"❌ 流程异常: {str(e)}")
        driver.save_screenshot('error_flow.png')
    return False

def main_loop():
    """增强主循环稳定性"""
    attempt = 0
    max_attempts = 200
    
    while attempt < max_attempts:
        try:
            intelligent_refresh()
            
            if check_availability():
                if booking_flow():
                    logging.info("💡 抢票成功，保持浏览器打开")
                    return
                else:
                    # 关闭当前窗口并恢复初始状态
                    driver.close()
                    driver.switch_to.window(driver.window_handles[0])
                    human_like_delay(2, 0.5)
                    
            attempt += 1
            delay = random.uniform(
                CONFIG['human_behavior']['min_delay'],
                CONFIG['human_behavior']['max_delay']
            )
            time.sleep(delay)
            
        except Exception as e:
            logging.error(f"⚠️ 主循环异常: {str(e)}")
            driver.refresh()
            human_like_delay(3)

    logging.warning("⏳ 已达最大尝试次数，退出监控")

if __name__ == "__main__":
    try:
        main_loop()
    except KeyboardInterrupt:
        logging.info("用户中断操作")
    finally:
        # 生产环境建议保留退出清理
        # driver.quit()
        pass    